﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using ICSharpCode.SharpZipLib.Zip;

namespace LaunchMeNot.Core
{
    public class Utils
    {
        /// <summary>
        /// Opens the help file. This avoids messy Windows.Forms usings in every file.
        /// </summary>
        /// <param name="pageName">Name of the page at which to open the help file or <c>null</c> for the contents page.</param>
        public static void OpenHelp(string pageName)
        {
            if (pageName == null) {
                System.Windows.Forms.Help.ShowHelp(null, "LaunchMeNot.chm");
            } else {
                System.Windows.Forms.Help.ShowHelp(null, "LaunchMeNot.chm", System.Windows.Forms.HelpNavigator.Topic, pageName);
            }
        }

        /// <summary>
        /// Waits for the BackgroundWorker to complete before returning.
        /// </summary>
        public static void WaitForWorker(BackgroundWorker worker)
        {
            if (worker != null && worker.IsBusy) {
                while (worker.IsBusy) {
                    Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate() { }));
                }
            }
        }

        /// <summary>
        /// Splits a path which may contain arguments into a fileename and the arguments.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns>A pair containing the file name as the key and the arguments as the value, or <c>null</c></returns>
        public static KeyValuePair<string, string> SplitArguments(string path) {
            string fileName = null,
                   args = string.Empty;
            if (!string.IsNullOrEmpty(path)) {
                string trimmed = path.Trim();
                int filePathEnd;
                if (trimmed[0] == '"') {
                    filePathEnd = trimmed.IndexOf('"', 1);
                    if(filePathEnd > 0) {
                        fileName = trimmed.Substring(1, filePathEnd - 1);
                        args = path.Substring(filePathEnd + 1).Trim();
                    } else {
                        fileName = trimmed;
                    }
                } else {
                    filePathEnd = trimmed.IndexOf(' ');
                    if (filePathEnd > 0) {
                        fileName = trimmed.Substring(0, filePathEnd);
                        if (filePathEnd < trimmed.Length) { // there are characters after the space
                            args = trimmed.Substring(filePathEnd);
                        }
                    } else {
                        fileName = trimmed;
                    }

                }
            }
            return new KeyValuePair<string, string>(fileName, args);
        }

        /// <summary>
        /// Extracts the executables from a zip file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="deleteAfter"><c>true</c> if the archive should be deleted on successful unzipping.</param>
        /// <returns>The names of extracted files.</returns>
        public static IEnumerable<string> ExtractAndDelete(string fileName, bool deleteAfter) {
            List<string> files = new List<string>();
            using(FileStream fs = File.OpenRead(fileName))
            using(ZipInputStream zipStream = new ZipInputStream(fs)) {
                ZipEntry entry;
                byte[] buffer = new byte[2048];
                string tempPath = Path.GetTempPath();
                while((entry = zipStream.GetNextEntry()) != null) {
                    string fileNameOnly = Path.GetFileName(entry.Name);
                    if (Path.GetExtension(entry.Name).ToLowerInvariant() == ".exe") {
                        string extractedFileName = Path.Combine(tempPath, fileNameOnly);
                        using (FileStream streamWriter = File.Create(extractedFileName)) {
                            byte[] data = new byte[2048];
                            int read = 0;
                            while ((read = zipStream.Read(data, 0, data.Length)) > 0) {
                                streamWriter.Write(data, 0, read);
                            }
                        }
                        files.Add(extractedFileName);
                    }
                }
            }
            if (deleteAfter)
                File.Delete(fileName);
            return files;
        }

        /// <summary>
        /// Find the first unique index of a string in a collection of strings. The incrementation starts at 2.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="otherNames">The other names.</param>
        /// <returns>The first unique index of a string in a collection of strings</returns>
        /// <example>
        /// Collection contains - name, name1, name2. 
        /// "name3" will be returned.
        /// </example>
        public static string GetNextAvailableName(string name, IEnumerable<string> otherNames)
        {
            if (!otherNames.Contains(name)) {
                return name;
            } else {
                bool gotName = false;
                for (int i = 2; !gotName; ) {
                    string currentName = name + ' ' + i;
                    if (otherNames.Contains(currentName)) {
                        i++;
                    } else {
                        name = currentName;
                        gotName = true;
                    }
                }
                return name;
            }
        }

        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SHELLEXECUTEINFO
        {
            public int cbSize;
            public uint fMask;
            public IntPtr hwnd;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpVerb;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpFile;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpParameters;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpDirectory;
            public int nShow;
            public IntPtr hInstApp;
            public IntPtr lpIDList;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpClass;
            public IntPtr hkeyClass;
            public uint dwHotKey;
            public IntPtr hIcon;
            public IntPtr hProcess;
        }
        private const int SW_SHOW = 5;
        private const uint SEE_MASK_INVOKEIDLIST = 12;

        /// <summary>
        /// Shows the explorer file properties dialog.
        /// </summary>
        /// <param name="Filename">The filename.</param>
        public static void ShowFileProperties(string Filename)
        {
            SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
            info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
            info.lpVerb = "properties";
            info.lpFile = Filename;
            info.nShow = SW_SHOW;
            info.fMask = SEE_MASK_INVOKEIDLIST;
            ShellExecuteEx(ref info);

        }

    }
}